using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace DanMuHelper.Cache
{
    /// <summary>
    /// 粒子特效缓存接口, 返回的实例销毁原则：超过缓存池上线，超过很长时间没有使用的（需要有一个保底？）
    /// </summary>
    public class CachedParticleSystem
    {
        private class Instance
        {
            public ParticleSystem ps;
            public int instanceID;
            public float returnTime;
        }

        private class AliveInstance
        {
            public ParticleSystem ps;
        }

        /// <summary>
        /// 预置体
        /// </summary>
        public ParticleSystem origin;

        /// <summary>
        /// 预制体原始缩放
        /// </summary>
        public Vector3 originScale = default;
        public Vector3 OriginScale
        {
            get
            {
                if (originScale == default)
                {
                    if (origin != null)
                    {
                        originScale = origin.transform.localScale;
                    }
                    else
                    {
                        originScale = Vector3.one;
                    }
                }
                return originScale;
            }
        }

        /// <summary>
        /// 粒子父节点
        /// </summary>
        public Transform parent;
        /// <summary>
        /// 强制使用一个父节点(在当前场景最上层创建一个父节点)
        /// </summary>
        public bool autoParent = true;
        /// <summary>
        /// 缓存系统自动创建的父节点
        /// </summary>
        private Transform _runtimeParent;
        /// <summary>
        /// 保底缓存数量
        /// </summary>
        public int guarantees = 4;
        /// <summary>
        /// 缓存容量上限
        /// </summary>
        public int capacity = 16;
        /// <summary>
        /// Time-to-live, 超过ttl时间内未使用的缓存对象会被销毁
        /// </summary>
        public float ttl = 20;
        /// <summary>
        /// 缓存池
        /// </summary>
        private List<Instance> pool;
        private List<AliveInstance> alive;

        private readonly List<ParticleSystem> fxPool;
        private readonly List<ParticleSystem> fxAlive;

        /// <summary>
        /// 创建粒子实例
        /// </summary>
        public ParticleSystem GetOrCreate(Vector3 position)
        {
            ParticleSystem result = null;
            do
            {
                var hitted = false;
                if (null == pool)
                {
                    hitted = true;
                    result = CreateNewParticle(origin, position, origin.transform.rotation);
                }
                var now = Time.unscaledDeltaTime;
                var n = null != pool ? pool.Count : 0;
                for (int i = 0; i < n && !hitted; i++)
                {
                    // 避免在同一帧回收，同一帧复用现象
                    if (Mathf.Abs(now - pool[i].returnTime) >= 0.2f)
                    {
                        hitted = true;
                        result = pool[i].ps;
                        pool.RemoveAt(i);
                        var trf = result.transform;
                        // trf.SetParent(parent, false);
                        //trf.SetParent(GetParent(), false);
                        trf.position = position;
                        result.gameObject.SetActive(true);
#if UNITY_EDITOR
                        // do{
                        //     // 调试
                        //     var comment = result.GetOrAddComponent<HierarchyComment>();
                        //     comment.comment = "复用";
                        // }while(false);
#endif
                        break;
                    }
                }
                if (!hitted)
                    result = CreateNewParticle(origin, position, origin.transform.rotation);
            } while (false);
            if (null == alive)
            {
                alive = new List<AliveInstance>(2);
            }
            alive.Add(new AliveInstance { ps = result });
            return result;
        }

        /// <summary>
        /// 创建粒子实例
        /// </summary>
        public ParticleSystem GetOrCreate(Vector3 position, Quaternion rotation)
        {
            ParticleSystem result = null;
            do
            {
                var hitted = false;
                if (null == pool)
                {
                    hitted = true;
                    result = CreateNewParticle(origin, position, rotation);
                }
                var now = Time.unscaledDeltaTime;
                var n = null != pool ? pool.Count : 0;
                for (int i = 0; i < n && !hitted; i++)
                {
                    // 避免在同一帧回收，同一帧复用现象
                    if (Mathf.Abs(now - pool[i].returnTime) >= 0.2f)
                    {
                        hitted = true;
                        result = pool[i].ps;
                        pool.RemoveAt(i);
                        var trf = result.transform;
                        // trf.SetParent(parent, false);
                        //trf.SetParent(GetParent(), false);
                        trf.SetPositionAndRotation(position, rotation);
                        result.gameObject.SetActive(true);
#if UNITY_EDITOR
                        // do{
                        //     // 调试
                        //     var comment = result.GetOrAddComponent<HierarchyComment>();
                        //     comment.comment = "复用";
                        // }while(false);
#endif
                        break;
                    }
                }
                if (!hitted)
                    result = CreateNewParticle(origin, position, rotation);
            } while (false);
            if (null == alive)
            {
                alive = new List<AliveInstance>(512);
            }
            alive.Add(new AliveInstance { ps = result });
            return result;
        }

        /// <summary>
        /// 返还粒子实例
        /// </summary>
        public void Return(ParticleSystem target)
        {
            // TODO: #lich# 验证返还的对象是否是通过缓存接口创建的？
            if (null != pool && pool.Count >= capacity)
            {
                DestroyParticle(target);
                return;
            }
            if (null == pool)
            {
                pool = new List<Instance>(4);
            }
            var instanceID = target.GetInstanceID();
            if (pool.FindIndex((item) => item.instanceID == instanceID) != -1)
            {
                // an error
                // SLog.Error("Particle already in cache!");
                return;
            }
            // TODO: #lich# 回收策略：直接禁用（依赖与PlayOnAwake属性）; 停止播放并移动到相机视野以外
            target.gameObject.SetActive(false);
            target.transform.localScale = OriginScale;
            var cacheItem = new Instance();
            cacheItem.ps = target;
            cacheItem.instanceID = instanceID;
            cacheItem.returnTime = Time.unscaledTime;
            // #if UNITY_EDITOR
            //             do{
            //                 // 调试
            //                 var comment = target.GetOrAddComponent<HierarchyComment>();
            //                 comment.comment = "cached";
            //             }while(false);
            // #endif
            pool.Add(cacheItem);
        }

        /// <summary>
        /// 缓存池修剪，需要定期调用一次
        /// </summary>
        public void Update()
        {
            if (null != alive)
            {
                // 自动回收
                // TODO: #lich# 注意：自动回收依赖于stopAction=Disable的情形
                for (int i = 0; i < alive.Count;)
                {
                    if (!alive[i].ps.gameObject.activeSelf)
                    {
                        Return(alive[i].ps);
                        alive.RemoveAt(i);
                    }
                    else
                    {
                        i++;
                    }
                }
            }
            if (ttl > 0 && null != pool)
            {
                var now = Time.unscaledTime;
                var counter = pool.Count;
                // 从保底数量开始，清理长时间未命中的缓存对象
                for (int i = guarantees; i < counter;)
                {
                    if (Mathf.Abs(now - pool[i].returnTime) >= ttl)
                    {
                        DestroyParticle(pool[i].ps);
                        pool.RemoveAt(i);
                        --counter;
                    }
                    else
                    {
                        i++;
                    }
                }
            }
        }

        /// <summary>
        /// 销毁所有缓存实例
        /// </summary>
        public void DestroyCachedParticles()
        {
            if (null == pool)
                return;
            for (int i = pool.Count - 1; i >= 0; i--)
            {
                UnityEngine.Object.Destroy(pool[i].ps.gameObject);
            }
            pool.Clear();
        }

        /// <summary>
        /// 销毁所有存活实例
        /// </summary>
        public void DestroyAliveParticles()
        {
            if (null == alive)
                return;

            for (int i = 0; i < alive.Count; i++)
            {
                ParticleSystem ps = alive[i].ps;
                if (ps != null)
                    DestroyParticle(ps);
            }

            alive.Clear();
        }

        public Transform GetParent()
        {
            var parent = this.parent;
            if (null == parent)
            {
                if (autoParent && null == _runtimeParent)
                {
                    var parentGo = new GameObject($"cache-{origin.name}-{origin.GetInstanceID()}");
                    _runtimeParent = parentGo.transform;
                }
                parent = _runtimeParent;
            }
            return parent;
        }

        ParticleSystem CreateNewParticle(ParticleSystem origin, Vector3 position, Quaternion rotation)
        {
            var result = UnityEngine.Object.Instantiate(origin, position, rotation);

            var main = result.main;
            main.stopAction = ParticleSystemStopAction.Disable;
            if (result.gameObject.activeSelf)
            {
                result.gameObject.SetActive(true);
            }
            return result;
        }

        void DestroyParticle(ParticleSystem ps)
        {
            UnityEngine.Object.Destroy(ps.gameObject);
        }
    }
}
